
import bpy, bgl, re, os

from bpy_extras.io_utils import ExportHelper
from bpy.props import *
from bpy.types import Menu, Operator
from bpy_extras.io_utils import ExportHelper

from struct import pack, unpack, calcsize
from mathutils import Matrix, Vector, Euler, Quaternion

from math import *
from collections import OrderedDict

class Animation(object):
    __slots__ = [
        "name",
        "scene",
        "armature",
        "data",
        "action",
        "frames",
        "frame_values",
        "frame_duration",
        "adj_mat"
        ]
        
    def __init__(self, scene, armature, action, adj_mat):
        from ossl._types.Scene import Scene
        self.scene = Scene(scene)
        self.adj_mat = adj_mat
        from ossl._types.Armature import Armature
        self.armature = Armature(armature, adj_mat)
        self.data = armature.animation_data
        self.action = action
        self.name = action.name
        self.frames = None
        self.frame_values = []
        self.frame_duration = 1.0 / 24.0
        
    def __repr__(self):
        return "<Animation[\"%s\",\"%s\",\"%s\"]>" % (self.scene.name, self.armature.name, self.name)

    def prepare_for_export(self):
        print("Animation preparing for Export")
        
        re_dp = re.compile(r"pose.bones\[\"(.*)\"\]\.(loc|rot|sca).*")
        curves = self.action.fcurves
        bone_dict = {}

        for curve in curves:
            m = re_dp.match(curve.data_path)
            bone_name = str(m.group(1)) # e.g. "mPelvis"
            if not bone_name in bone_dict: bone_dict[bone_name] = {}
            parms = bone_dict[bone_name]
            bone_parm_type = str(m.group(2)) # e.g. "rot"
            bone = self.armature.bones[bone_name]
            exec("bone.use_" + bone_parm_type + "=True")
            #eval("self.armature.bones[\"" + bone_name + "\"].use_" + bone_parm_type + "
            if not bone_parm_type in parms: parms[bone_parm_type] = {}
            parm_frames = parms[bone_parm_type]
            frame_nrs = [int(kfp.co[0]) for kfp in curve.keyframe_points]
            for frame_nr in frame_nrs: parm_frames[frame_nr] = True
        
        f_min = 2^31
        f_max = -f_min

        for bone_name in bone_dict:
            print(bone_name)

            bone_data = bone_dict[bone_name]
            for bone_parm_type in bone_data:
                parm_frames = bone_data[bone_parm_type].keys()
                fmin = min(parm_frames)
                fmax = max(parm_frames)
                f_min = min([f_min, fmin])
                f_max = max([f_max, fmax])
                print("\t", bone_parm_type, fmin, fmax, tuple(v for v in parm_frames))

        print("min", f_min, ", max", f_max)
        self.frames = range(int(f_min), int(f_max) + 1)
        
        for bone_name in bone_dict:
            self.armature.bones[bone_name].set_mandatory()

        self.frame_duration = 1.0 / 6.0
        self.compute_frames()
        print("Animation prepared for Export")
        return self

    def update_posedata(self):
        for bone in self.armature.root_bones:
            bone.recursive_update_posedata()

        for bone in self.armature.root_bones:
            bone.recursive_compute_frame()

    def compute_frames(self):
        frame_current = self.scene.scene.frame_current

        for frame in self.frames:
            self.scene.scene.frame_set(frame)
            self.update_posedata()

        self.scene.scene.frame_set(frame_current)

    def mark_usage(self):
        loc0, rot0 = self.frame_values[0]
        self.use_loc, self.use_rot = (False, False)
        
        def differs(v1, v2):
            if type(v1) is float: return abs(v2-v1) > 0.0005
            d = False
            for i in range(0, len(v1)): d = d or differs(v1[i], v2[i])
            return d

        for i in range(0, len(self.frame_values)):
            loc, rot = self.frame_values[i]
            if ((not self.use_loc) and differs(loc, loc0)): self.use_loc = True
            if ((not self.use_rot) and differs(rot, rot0)): self.use_rot = True

        for bone in self.children:
            bone.mark_usage()

        if (self.use_loc or self.use_rot):
            self.set_mandatory()
            
import ossl.menu as menu

class OSSL_MT_Animation(menu.MenuFolder):
    bl_label = "OSSL Menu Animation"
    bl_idname = "OSSL_MT_Animation"

def OSSL_menu_Animation(self, comtext):
    self.layout.menu("OSSL_MT_Animation", text="Animation", icon="ANIM")

import ossl.util as util

def register():
    print("registering: OSSL Type Animation")
    bpy.utils.register_class(OSSL_MT_Animation)
    bpy.types.TOPBAR_MT_OSSL.append(OSSL_menu_Animation)
    util.imports("ossl._types.Animation", "export")
    util.imports("ossl._types.Animation", "import")
    print("registered: OSSL Type Animation")

def unregister():
    print("unregistering: OSSL Type Animation")
    util.release("ossl._types.Animation", "import")
    util.release("ossl._types.Animation", "export")
    bpy.types.TOPBAR_MT_OSSL.remove(OSSL_menu_Animation)
    bpy.utils.unregister_class(OSSL_MT_Animation)
    print("unregistered: OSSL Type Animation")

if __name__ == "__main__":
    util.reset_addon()
